package com.bestcem.xm.ticket.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateException;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.*;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.ticket.constant.Constants;
import com.bestcem.xm.ticket.enums.*;
import com.bestcem.xm.ticket.service.dto.mq.*;
import com.bestcem.xm.ticket.service.dto.ticket.rule.ValueEntityDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 问卷答案校验工具类
 *
 * @author jy.zhao
 * @version 1.0
 * @date 2021/8/18 15:04
 **/
@Slf4j
public class SurveyAnswerValidUtil {

    private SurveyAnswerValidUtil() {
    }

    /**
     * 下划线分隔符
     */
    private static final String UNDERLINE_SEPARATOR = "_";

    /**
     * 点分隔符
     */
    private static final String POINT_SEPARATOR = "\\.";

    /**
     * 分数
     */
    private static final String SCORE = "score";

    /**
     * 默认分数
     */
    private static final Integer DEFAULT_SCORE = 0;

    /**
     * 选项
     */
    private static final String OPTION = "options";

    /**
     * 选项匹配规则
     */
    private static final String OPTION_MATCH_RULE = "optionMatch";

    /**
     * 题目id在map中的标识
     */
    private static final String QTYPE = "qtype";

    /**
     * 题目id在map中的标识
     */
    private static final String GID = "gid";
    /**
     * 答卷状态
     */
    private static final String RESPONDENT_STATUS = "rspd_status";

    /**
     * nps或只有一个选项的打分题
     */
    private static final Set<Integer> RESPONDENT_STATUS_OPEN_BEGIN = new HashSet<>(Arrays.asList(RespondentStatusEnum.OPEN.getIndex(),RespondentStatusEnum.BEGIN.getIndex()) );

    /**
     * nps或只有一个选项的打分题
     */
    private static final List<String> QUESTION_TYPE_LIST = Arrays.asList(TitleTypeEnum.SCORE.getName(), TitleTypeEnum.NPS.getName(), TitleTypeEnum.PROPORTION.getName());

    /**
     * 根据答卷答案去校验工单预警条件
     *
     * @param filterDTO        预警条件
     * @param answer           答案
     * @param respondentStatus 答卷状态
     * @author jy.zhao
     * @date 2021/8/18 13:41
     **/
    public static Map<String, Object> validTicketCondition(FilterDTO filterDTO, Map<String, Object> answer, Integer respondentStatus) {
        // 默认答卷状态已完成
        if (Validator.isNotNull(filterDTO.getCategory()) && filterDTO.getCategory() == TicketConditionCategoryEnum.RESPONDENT_STATUS_CHANGE.getIndex()) {
            // key:rspd_status,value:为答卷状态
            return validRspdStatusChangeCondition(filterDTO, respondentStatus);
        }
        Integer operation = filterDTO.getOperation();
        if (Validator.isNull(operation)) {
            return null;
        }
        Map<String, Object> result = null;
        if (SurveyTitleTypeEnum.WORD.getIndex().equals(operation)) {
            result = validDangerousWordAnswer(filterDTO, answer);
        } else if (SurveyTitleTypeEnum.SCORE_RANGE.getIndex().equals(operation) || SurveyTitleTypeEnum.AVERAGE.getIndex().equals(operation) ||
                SurveyTitleTypeEnum.SUM.getIndex().equals(operation)) {
            result = validScoreTitleAnswer(filterDTO, answer);
        } else if (SurveyTitleTypeEnum.SINGLE.getIndex().equals(operation)) {
            result = validSingleTitleAnswer(filterDTO, answer);
        } else if (SurveyTitleTypeEnum.SELECT.getIndex().equals(operation) || SurveyTitleTypeEnum.UNSELECT.getIndex().equals(operation) ||
                SurveyTitleTypeEnum.MULTIPLE.getIndex().equals(operation)) {
            result = validChooseTitleAnswer(filterDTO, answer);
        } else if (SurveyTitleTypeEnum.EXIST.getIndex().equals(operation)) {
            result = validExistTitleAnswer(filterDTO, answer);
        } else if (SurveyTitleTypeEnum.CLASSIFY.getIndex().equals(operation) || SurveyTitleTypeEnum.UNCLASSIFY.getIndex().equals(operation)) {
            result = validClassifyTitleAnswer(filterDTO, answer);
        } else if (SurveyTitleTypeEnum.ORDER.getIndex().equals(operation)) {
            result = validOrderTitleAnswer(filterDTO, answer);
        } else if (SurveyTitleTypeEnum.TIME.getIndex().equals(operation) || SurveyTitleTypeEnum.DATE.getIndex().equals(operation)) {
            result = validDateTitleAnswer(filterDTO, answer);
        }
        if (log.isInfoEnabled()) {
            log.info("[ticket] 检查工单预警条件. 题型：{}， 检查结果：{}", operation, result);
        }
        return result;
    }

    /**
     * 检查类别为答卷状态变化的条件
     *
     * @param filterDTO        预警／工单条件
     * @param respondentStatus 答卷状态
     * @return Map<String, Object> object 为答卷状态
     * @author jy.zhao
     * @date 2021/8/18 13:53
     **/
    private static Map<String, Object> validRspdStatusChangeCondition(FilterDTO filterDTO, Integer respondentStatus) {
        JSONObject value = toJSONObject(filterDTO.getValue());
        Long expireTime = Objects.isNull(value) ? null : value.getLong("expire");
        if (log.isInfoEnabled()) {
            log.info("[ticket] 过期时间：{}, 答卷状态：{}， 条件： {}", expireTime, respondentStatus, filterDTO);
        }
        if (Validator.isNull(expireTime)) {
            return null;
        }
        Long currentSecond = DateUtil.toSecond(DateUtil.getCurrentTime());
        if (log.isInfoEnabled()) {
            log.info("[ticket] 当前时间：{}", currentSecond);
        }
        // 如果expireTime过期，返回
        if (expireTime > currentSecond) {
            return null;
        }
        Map<String, Object> result = new HashMap<>();
        Integer operation = filterDTO.getOperation();
        // 操作题型未提交
        if (SurveyTitleTypeEnum.RESPONDENT_BEGIN.getIndex().equals(operation)) {
            // 答卷是已打开或进行中
            if (Validator.isNull(respondentStatus) || RESPONDENT_STATUS_OPEN_BEGIN.contains(respondentStatus)) {
                result.put(RESPONDENT_STATUS, respondentStatus);
            }
        } else if (SurveyTitleTypeEnum.RESPONDENT_SUBMIT.getIndex().equals(operation)) {
            // 操作题型已提交
            // 答卷是已打开或进行中
            if (Validator.isNotNull(respondentStatus) && (!RESPONDENT_STATUS_OPEN_BEGIN.contains(respondentStatus))) {
                result.put(RESPONDENT_STATUS, respondentStatus);
            }
        } else if (SurveyTitleTypeEnum.RESPONDENT_EMPTY.getIndex().equals(operation) && (Validator.isNull(respondentStatus) || respondentStatus == RespondentStatusEnum.OPEN.getIndex())) {
            // 操作题型未开始，答卷是已打开
            result.put(RESPONDENT_STATUS, respondentStatus);
        }
        return result;
    }

    /**
     * 处理危险词
     *
     * @param filterDTO 条件
     * @param answer    答案
     * @return Map<String, Object> 返回的数据格式： {gid:{operation: 1, data: {}}}
     * @author jy.zhao
     * @date 2021/8/25 21:36
     **/
    public static Map<String, Object> validDangerousWordAnswer(FilterDTO filterDTO, Map<String, Object> answer) {
        // 解析字符串，获取问题源.可能为数字数组，也可能为对象数组
        JSONArray sourceQList = toJSONArray(filterDTO.getSourceQ());
        Map<String, Object> checkAnswerMap = new HashMap<>();
        if (!Objects.isNull(sourceQList) && !sourceQList.isEmpty()) {

            // 只检查一道题,获取题目选项
            QuestionOptionDTO questionOptionDTO = getSourceQuestionOption(sourceQList.get(0));
            String gid = questionOptionDTO.getGid();
            if (answer.containsKey(gid)) {
                String rowId = "";
                String seq = "";
                // 是否是评价题
                if (isEvaluateTitle(sourceQList.get(0))) {
                    seq = "open";
                } else if (CollUtil.isNotEmpty(questionOptionDTO.getOptionIdDTOs())) {
                    List<OptionIdDTO> options = questionOptionDTO.getOptionIdDTOs();
                    if (CollUtil.isNotEmpty(options)) {
                        rowId = options.get(0).getRow_id();
                        seq = options.get(0).getOption_id();
                    }
                }
                try {
                    if (Validator.isNotEmpty(rowId)) {
                        if (isInstance(answer.get(gid), DataTypeEnum.MAP) && isInstance(toMap(answer.get(gid)).get(rowId), DataTypeEnum.LIST)) {
                            if (isMatrixSingle(sourceQList.get(0))) {
                                checkAnswerMap.put(gid, MapUtil.of(rowId, MapUtil.of(seq, toList(toMap(answer.get(gid)).get(rowId)).get(1))));
                            } else if (isMatrixMultiple(sourceQList.get(0))) {
                                for (Object item : toList(toMap(answer.get(gid)).get(rowId))) {
                                    if (isInstance(item, DataTypeEnum.LIST) && toList(item).get(0).toString().equals(seq)) {
                                        checkAnswerMap.put(gid, MapUtil.of(rowId, MapUtil.of(seq, toList(item).get(1))));
                                        break;
                                    }
                                }
                            }
                        } else {
                            checkAnswerMap.put(gid, MapUtil.of(rowId, MapUtil.of(seq, toMap(toMap(answer.get(gid)).get(rowId)).get(seq))));
                        }
                    } else if (Validator.isNotEmpty(seq)) {
                        if (isInstance(answer.get(gid), DataTypeEnum.LIST)) {
                            if (isSingleTitle(sourceQList.get(0))) {
                                checkAnswerMap.put(gid, MapUtil.of(toList(answer.get(gid)).get(0), Collections.singletonList(toList(answer.get(gid)).get(1))));
                            } else if (isMultiple(sourceQList.get(0))) {
                                for (Object item : toList(answer.get(gid))) {
                                    if (isInstance(item, DataTypeEnum.LIST) && toList(item).get(0).toString().equals(seq)) {
                                        checkAnswerMap.put(gid, MapUtil.of(toList(item).get(0), Collections.singletonList(toList(item).get(1))));
                                        break;
                                    }
                                }
                            }
                        } else {
                            checkAnswerMap.put(gid, MapUtil.of(seq, toMap(answer.get(gid)).get(seq)));
                        }
                    } else {
                        checkAnswerMap.put(gid, answer.get(gid));
                    }
                } catch (Exception e) {
                    log.error("[ticket] 检查危险字查找答案错误rowId={}, gid={}, seq={}", rowId, gid, seq, e);
                }
            }
        }
        Map<String, Object> result = new HashMap<>();
        checkAnswerMap.forEach((gid, data) -> {
            Map<String, Object> words = checkWord(data, filterDTO.getValue(), "");
            if (MapUtil.isNotEmpty(words)) {
                TitleHandleResultDTO titleHandleResultDTO = new TitleHandleResultDTO(SurveyTitleTypeEnum.WORD.getIndex(), words);
                result.put(gid, titleHandleResultDTO);
            }

        });
        return result;
    }

    /**
     * 处理分数题目
     *
     * @param filterDTO 条件
     * @param answer    答案
     * @return Map<String, Object> 返回值
     * @author jy.zhao
     * @date 2021/8/19 21:11
     **/
    public static Map<String, Object> validScoreTitleAnswer(FilterDTO filterDTO, Map<String, Object> answer) {
        // 1.获取原始问题
        Map<String, Object> result = new HashMap<>();
        JSONArray sourceQList = toJSONArray(filterDTO.getSourceQ());
        if (Objects.isNull(sourceQList) || sourceQList.isEmpty()) {
            return result;
        }
        // 题目分数
        int titleScore = 0;
        // 选项数量
        int optionCount = 0;
        Map<String, List<Object>> rstData = new HashMap<>();
        // 2.得到题目类型
        for (Object sourceQ : sourceQList) {
            QuestionOptionDTO questionOptionDTO = getSourceQuestionOption(sourceQ);
            String gid = questionOptionDTO.getGid();
            // 根据问题id获取问题答案
            Object questionAnswer = answer.get(gid);
            if (Validator.isNull(questionAnswer)) {
                continue;
            }
            if (isEvaluateTitle(sourceQ)) {
                if (isInstance(questionAnswer, DataTypeEnum.MAP)) {
                    titleScore += Integer.parseInt(toMap(questionAnswer).getOrDefault(SCORE, DEFAULT_SCORE).toString());
                }
                HashMap<String, String> scoreMap = new HashMap<>();
                scoreMap.put("rid", null);
                scoreMap.put("oid", SCORE);
                List<Object> value = rstData.getOrDefault(gid, new ArrayList<>());
                value.add(scoreMap);
                rstData.put(gid, value);
                optionCount++;
            } else {
                List<OptionIdDTO> options = questionOptionDTO.getOptionIdDTOs();
                for (OptionIdDTO optionIdDTO : options) {
                    titleScore += getScore(questionAnswer, optionIdDTO.getOption_id(), optionIdDTO.getRow_id());
                    HashMap<String, String> scoreMap = new HashMap<>();
                    scoreMap.put("rid", optionIdDTO.getRow_id());
                    scoreMap.put("oid", optionIdDTO.getOption_id());
                    List<Object> value = rstData.getOrDefault(gid, new ArrayList<>());
                    value.add(scoreMap);
                    rstData.put(gid, value);
                    optionCount++;
                }
            }
        }
        if (SurveyTitleTypeEnum.AVERAGE.getIndex().equals(filterDTO.getOperation()) && optionCount > 0) {
            titleScore = titleScore / optionCount;
        }
        ScoreRangeDTO scoreRangeDTO = JSON.parseObject(filterDTO.getValue(), ScoreRangeDTO.class);
        if (validScoreRange(titleScore, scoreRangeDTO)) {
            rstData.forEach((gid, data) -> {
                TitleHandleResultDTO titleHandleResultDTO = new TitleHandleResultDTO(filterDTO.getOperation(), data);
                result.put(gid, titleHandleResultDTO);
            });
        }
        return result;
    }

    /**
     * 处理单选题目
     *
     * @param filterDTO 条件
     * @param answer    答案
     * @return Map<String, Object>
     * @author jy.zhao
     * @date 2021/8/25 21:37
     **/
    public static Map<String, Object> validSingleTitleAnswer(FilterDTO filterDTO, Map<String, Object> answer) {
        Map<String, Object> result = new HashMap<>();
        // 1.获取原始问题
        JSONArray sourceQList = toJSONArray(filterDTO.getSourceQ());
        if (Objects.isNull(sourceQList) || sourceQList.isEmpty()) {
            return result;
        }
        String gid = getQuestionId(sourceQList.get(0));
        // 4.答案中没有这个题目
        if (!answer.containsKey(gid)) {
            return result;
        }
        // 答卷答案中的选项
        Set<String> optionIdList = getOptionList(answer.get(gid));
        JSONObject value = toJSONObject(filterDTO.getValue());
        if (Objects.isNull(value)) {
            return result;
        }
        List<String> optionAnswerList = new ArrayList<>();
        if (isInstance(value.get(OPTION), DataTypeEnum.LIST)) {
            toList(value.get(OPTION)).forEach(item -> optionAnswerList.add(item.toString()));
        }
        // 取交集
        optionIdList.retainAll(optionAnswerList);
        if (!optionIdList.isEmpty()) {
            TitleHandleResultDTO titleHandleResultDTO = new TitleHandleResultDTO(filterDTO.getOperation(), optionIdList);
            result.put(gid, titleHandleResultDTO);
        }
        return result;
    }

    /**
     * 处理选择提
     *
     * @param filterDTO 条件
     * @param answer    答案
     * @return Map<String, Object>
     * @author jy.zhao
     * @date 2021/8/20 10:56
     **/
    public static Map<String, Object> validChooseTitleAnswer(FilterDTO filterDTO, Map<String, Object> answer) {
        Map<String, Object> result = new HashMap<>();
        // 1. 获取题目信息
        JSONArray sourceQList = toJSONArray(filterDTO.getSourceQ());
        if (Objects.isNull(sourceQList) || sourceQList.isEmpty()) {
            return result;
        }
        Object sourceQ = sourceQList.get(0);
        // 2. 得到题目类型
        Integer titleType = filterDTO.getOperation();
        // 3. 获取题目中的选项
        QuestionOptionDTO questionOptionDTO = getSourceQuestionOption(sourceQ);
        String gid = questionOptionDTO.getGid();
        // 如果没有gid题目对应的答案
        if (!answer.containsKey(gid)) {
            return result;
        }
        // 4. 获取预警或工单的配置值
        JSONObject valueObject = toJSONObject(filterDTO.getValue());
        if (Objects.isNull(valueObject)) {
            return result;
        }
        // 5. 根据题目获取问卷中的答案
        Object checkAnswer = answer.get(gid);
        String rowId = null;
        // 6. 配置中需要触发预警的选项
        List<OptionIdDTO> alertOptions = questionOptionDTO.getOptionIdDTOs();
        // 兼容2.2之前的数据
        if (!(isInstance(sourceQ, DataTypeEnum.MAP))) {
            if (CollUtil.isNotEmpty(alertOptions)) {
                rowId = alertOptions.get(0).getOption_id();
            }
            List<String> optionList = JSON.parseArray(filterDTO.getValue(), String.class);
            if (CollUtil.isNotEmpty(optionList)) {
                for (String opId : optionList) {
                    OptionIdDTO optionIdDTO = new OptionIdDTO(rowId, opId);
                    alertOptions.add(optionIdDTO);
                }
            }
        }
        // 7. 获取答案中的选项
        List<OptionIdDTO> answerOptionList = new ArrayList<>();
        // 旧数据
        if (Validator.isNotNull(rowId) && isInstance(checkAnswer, DataTypeEnum.MAP)) {
            Object oldCheckAnswer = toMap(checkAnswer).get("row_id");
            Set<String> optionList = getOptionList(oldCheckAnswer);
            for (String op : optionList) {
                answerOptionList.add(new OptionIdDTO(rowId, op));
            }
        } else if (isEvaluateTitle(sourceQ)) {
            // 评价题目
            if (isInstance(checkAnswer, DataTypeEnum.MAP) && toMap(checkAnswer).containsKey("tagsId")) {
                List<String> tags = JSON.parseArray(toMap(checkAnswer).get("tagsId").toString(), String.class);
                for (String tag : tags) {
                    answerOptionList.add(new OptionIdDTO(null, tag));
                }
            } else if (isInstance(checkAnswer, DataTypeEnum.MAP) && toMap(checkAnswer).containsKey("tags")) {
                List<String> tags = JSON.parseArray(toMap(checkAnswer).get("tags").toString(), String.class);
                for (String tag : tags) {
                    answerOptionList.add(new OptionIdDTO(null, tag));
                }
            }
        } else if (isInstance(checkAnswer, DataTypeEnum.MAP)) {
            // 矩阵选择题
            toMap(checkAnswer).forEach((seq, ans) -> {
                // seq为矩阵行gid
                Set<String> optionList = getOptionList(ans);
                optionList.forEach(op ->
                        answerOptionList.add(new OptionIdDTO(seq, op))
                );
            });
        } else {
            // 普通选择题，添加答案中的选项到selOptions
            getOptionList(checkAnswer).forEach(op -> answerOptionList.add(new OptionIdDTO(null, op)));
        }
        //
        Set<OptionIdDTO> alertOptionList = new HashSet<>(alertOptions);
        // 一种是未选中，一种是已选中  selOptions:答案中的选项。 alertOptionList:预警条件的选项
        if (SurveyTitleTypeEnum.UNSELECT.getIndex().equals(titleType)) {
            alertOptionList.removeAll(answerOptionList);
        } else {
            // 选中， 取包含关系
            alertOptionList.retainAll(answerOptionList);
        }
        // 0 全部选项 1 任一选项
        int optionMatch = Integer.parseInt(valueObject.getOrDefault(OPTION_MATCH_RULE, 1).toString());
        // 任一选项，只要optionList不为空，即触发
        if (optionMatch == 1 && !alertOptionList.isEmpty()) {
            List<Object> options = new ArrayList<>();
            alertOptionList.forEach(item -> options.add(dumpOption(item)));
            TitleHandleResultDTO titleHandleResultDTO = new TitleHandleResultDTO(titleType, options);
            result.put(gid, titleHandleResultDTO);
        }
        // 全部选项 alertOptionList为交集或差集后的结果，alertOptions为之前的结果
        if (optionMatch <= 0 && !alertOptionList.isEmpty() && alertOptionList.size() == alertOptions.size()) {
            List<Object> options = new ArrayList<>();
            alertOptionList.forEach(item -> options.add(dumpOption(item)));
            TitleHandleResultDTO titleHandleResultDTO = new TitleHandleResultDTO(titleType, options);
            result.put(gid, titleHandleResultDTO);
        }
        return result;
    }

    /**
     * 处理非空选项
     *
     * @param filterDTO 条件
     * @param answer    答案
     * @return Map<String, Object>
     * @author jy.zhao
     * @date 2021/8/20 11:20
     **/
    public static Map<String, Object> validExistTitleAnswer(FilterDTO filterDTO, Map<String, Object> answer) {
        Map<String, Object> result = new HashMap<>();
        // 解析字符串，获取问题源.可能为数字数组，也可能为对象数组
        JSONArray sourceQList = toJSONArray(filterDTO.getSourceQ());
        // 2.得到题目类型
        Integer titleType = filterDTO.getOperation();
        if (Objects.isNull(sourceQList) || sourceQList.isEmpty()) {
            return result;
        }
        // 只检查一道题,获取题目选项
        Object sourceQ = sourceQList.get(0);
        QuestionOptionDTO questionOptionDTO = getSourceQuestionOption(sourceQ);
        String gid = questionOptionDTO.getGid();
        // 4.答案中没有这个题目
        if (!answer.containsKey(gid)) {
            return result;
        }
        Object checkAnswer = answer.get(gid);
        String rowId = null;
        if (isEvaluateTitle(sourceQ)) {
            // 检查评价题开放框
            checkAnswer = toMap(checkAnswer).get("open");
        } else if (isSingleTitle(sourceQ)) {
            // 单选题开放框
            checkAnswer = Collections.singletonList(checkAnswer);
        } else if (isCityTitle(sourceQ)) {
            // 城市地理题 详细地址
            checkAnswer = toMap(checkAnswer).get("4");
        } else if (CollUtil.isNotEmpty(questionOptionDTO.getOptionIdDTOs())) {
            OptionIdDTO optionIdDTO = questionOptionDTO.getOptionIdDTOs().get(0);
            rowId = optionIdDTO.getRow_id();
            if (Validator.isNotNull(rowId) && isInstance(checkAnswer, DataTypeEnum.MAP)) {
                checkAnswer = toMap(checkAnswer).get(rowId);
            }
            if (isInstance(checkAnswer, DataTypeEnum.MAP)) {
                checkAnswer = toMap(checkAnswer).get(optionIdDTO.getOption_id());
            }
        }
        // 得到需要检查的答案
        if (Validator.isNotNull(checkAnswer) && isInstance(checkAnswer, DataTypeEnum.LIST)) {
            // 矩阵单选、排序题 answer [ 20, "1" ]
            if (isChoiceTitle(sourceQ)) {
                if (toList(checkAnswer).size() > 1) {
                    checkAnswer = toList(checkAnswer).get(1);
                } else {
                    checkAnswer = null;
                }
            } else if (isSingleMultipleTitle(sourceQ)) {
                if (isInstance(toList(checkAnswer).get(0), DataTypeEnum.LIST) && toList(toList(checkAnswer).get(0)).size() > 1) {
                    checkAnswer = toList(toList(checkAnswer).get(0)).get(1);
                } else {
                    checkAnswer = null;
                }
            } else {
                // 取出填空内容，数组其余部分为开放答案
                // 多项填空answer ['a']
                checkAnswer = toList(checkAnswer).get(0);
            }
        }

        if (!Objects.isNull(checkAnswer)) {
            HashMap<String, Object> data = new HashMap<>();
            TitleHandleResultDTO titleHandleResultDTO = new TitleHandleResultDTO(titleType, data);
            result.put(gid, titleHandleResultDTO);
            if (Validator.isNotNull(rowId)) {
                data.put(rowId, new HashMap<>());
            }
            String optionId = "";
            if (CollUtil.isNotEmpty(questionOptionDTO.getOptionIdDTOs()) && Validator.isNotEmpty(questionOptionDTO.getOptionIdDTOs().get(0).getOption_id())) {
                optionId = questionOptionDTO.getOptionIdDTOs().get(0).getOption_id();
            }
            String key = optionId + "#0";
            if (isInstance(checkAnswer, DataTypeEnum.STRING)) {
                data.put(key, Collections.singletonList(Arrays.asList(0, checkAnswer.toString().length())));
            } else {
                data.put(key, Collections.singletonList(Arrays.asList(0, 0)));
            }
        }
        return result;
    }

    /**
     * 处理分类和未分类题目
     *
     * @param filterDTO 条件
     * @param answer    答案
     * @return Map<String, String>
     * @author jy.zhao
     * @date 2021/8/19 15:30
     **/
    public static Map<String, Object> validClassifyTitleAnswer(FilterDTO filterDTO, Map<String, Object> answer) {
        Map<String, Object> result = new HashMap<>();
        // 1.获取原始问题
        JSONArray sourceQList = toJSONArray(filterDTO.getSourceQ());
        if (Objects.isNull(sourceQList) || sourceQList.isEmpty()) {
            return result;
        }
        // 2.得到题目类型
        Integer titleType = filterDTO.getOperation();
        // 只检查一道题,获取题目选项
        QuestionOptionDTO questionOptionDTO = getSourceQuestionOption(sourceQList.get(0));
        String gid = questionOptionDTO.getGid();
        // 3.答案中没有这个题目
        if (!answer.containsKey(gid)) {
            return result;
        }
        JSONObject value = toJSONObject(filterDTO.getValue());
        if (Objects.isNull(value)) {
            return result;
        }
        // 4. 获取配置的分类信息
        Object classifyOption = value.get("classify");
        if (CollUtil.isEmpty(questionOptionDTO.getOptionIdDTOs()) || Validator.isNull(classifyOption)) {
            return result;
        }
        if (!(isInstance(answer.get(gid), DataTypeEnum.MAP)) || (!(isInstance(classifyOption, DataTypeEnum.MAP)))) {
            return result;
        }
        // 5. 获取题目id和答案
        String classifyOptionGid = toMap(classifyOption).getOrDefault(GID, Constants.UNSPECIFIED_GID).toString();
        Object checkAnswer = toMap(answer.get(gid)).get(classifyOptionGid);
        // 6. 获取要触发的分类选项Id。titleType分为未分类和分类。取交集和差集
        Set<String> alertOptionIds = questionOptionDTO.getOptionIdDTOs().stream().map(OptionIdDTO::getOption_id).collect(Collectors.toSet());
        if (SurveyTitleTypeEnum.UNCLASSIFY.getIndex().equals(titleType)) {
            alertOptionIds.removeAll(getOptionList(checkAnswer));
        } else {
            alertOptionIds.retainAll(getOptionList(checkAnswer));
        }
        // 7. 选项匹配 0 全部选项 1 任一选项
        int optionMatch = Integer.parseInt(value.getOrDefault(OPTION_MATCH_RULE, 1).toString());
        if (optionMatch == 1 && !alertOptionIds.isEmpty()) {
            result.put(gid, new TitleHandleResultDTO(titleType, alertOptionIds));
        }
        if (optionMatch <= 0 && !alertOptionIds.isEmpty() && alertOptionIds.size() == questionOptionDTO.getOptionIdDTOs().size()) {
            result.put(gid, new TitleHandleResultDTO(titleType, alertOptionIds));
        }
        return result;
    }

    /**
     * 处理排序题目
     *
     * @param filterDTO 条件
     * @param answer    答案
     * @return Map<String, String>
     * @author jy.zhao
     * @date 2021/8/19 15:30
     **/
    public static Map<String, Object> validOrderTitleAnswer(FilterDTO filterDTO, Map<String, Object> answer) {
        Map<String, Object> result = new HashMap<>();
        // 1.获取原始问题
        JSONArray sourceQList = toJSONArray(filterDTO.getSourceQ());
        // 2.得到题目类型
        Integer titleType = filterDTO.getOperation();
        if (Objects.isNull(sourceQList) || sourceQList.isEmpty()) {
            return result;
        }
        // 3.从一个题目中获取题目id
        Object sourceQ = sourceQList.get(0);
        String gid = getQuestionId(sourceQ);
        // 4.答案中没有这个题目
        if (!answer.containsKey(gid) || !isInstance(sourceQ, DataTypeEnum.MAP)) {
            return result;
        }
        JSONObject value = toJSONObject(filterDTO.getValue());
        List<OrderTitleDTO> optionList = JSON.parseArray(toMap(sourceQ).get(OPTION).toString(), OrderTitleDTO.class);
        if (CollUtil.isEmpty(optionList)) {
            return result;
        }
        String sort = Objects.isNull(value) || Objects.isNull(value.getString("sort")) ? "1" : value.getString("sort");
        String seq = optionList.get(0).getGid();
        Object checkAnswer = answer.get(gid);
        if (Validator.isNotNull(checkAnswer) && isInstance(checkAnswer, DataTypeEnum.MAP) && toMap(checkAnswer).containsKey(seq)) {
            String sortAnswer = toMap(checkAnswer).get(seq).toString();
            if (sortAnswer.equals(sort)) {
                TitleHandleResultDTO resultDTO = new TitleHandleResultDTO(titleType, optionList);
                result.put(gid, resultDTO);
            }
        }

        return result;
    }

    /**
     * 处理时间题目
     *
     * @param filterDTO 条件
     * @param answer    答案
     * @return Map<String, String>
     * @author jy.zhao
     * @date 2021/8/19 15:29
     **/
    public static Map<String, Object> validDateTitleAnswer(FilterDTO filterDTO, Map<String, Object> answer) {
        Map<String, Object> result = new HashMap<>();
        // 1.获取原始问题
        JSONArray sourceQuestionList = toJSONArray(filterDTO.getSourceQ());
        if (Objects.isNull(sourceQuestionList) || sourceQuestionList.isEmpty()) {
            return result;
        }
        // 2.得到题目类型
        Integer titleType = filterDTO.getOperation();
        // 3.从一个题目中获取题目id
        String gid = getQuestionId(sourceQuestionList.get(0));
        // 4.答案中没有这个题目
        if (!answer.containsKey(gid)) {
            return result;
        }
        // 5.根据gid获取问卷中的题目答案
        Object checkAnswer = answer.get(gid);
        // 6.获取时间题目的时间和日期
        List<String> timeRange = new ArrayList<>();
        JSONArray timeArray;
        JSONObject value = toJSONObject(filterDTO.getValue());
        if (Objects.isNull(value)) {
            return result;
        }
        if (SurveyTitleTypeEnum.TIME.getIndex().equals(titleType)) {
            // 6.1题目中提及的时间范围
            timeArray = value.getJSONArray("time");
        } else {
            // 6.2题目中提及的日期范围
            timeArray = value.getJSONArray("date");
        }
        timeArray.forEach(item -> timeRange.add(item.toString()));
        if ((isInstance(checkAnswer, DataTypeEnum.LIST)) && CollUtil.isNotEmpty((Collection<?>) checkAnswer) && isInstance(toList(checkAnswer).get(0), DataTypeEnum.INTEGER)) {
            // 7.答案对应的秒数
            int answerSecond = (Integer) toList(checkAnswer).get(0);
            // 8.判断选择的答案是否在时间限制范围内
            if (validateTimeRange(answerSecond, timeRange, titleType)) {
                result.put(gid, new TitleHandleResultDTO(titleType, null));
            }
        }
        return result;
    }

    /**
     * 危险词检查.处理匹配的危险词列表和
     *
     * @param answer   单题答案
     * @param valueStr 字符串
     * @param checkKey key
     * @return Map<String, Object>
     * @author jy.zhao
     * @date 2021/8/18 17:01
     **/
    public static Map<String, Object> checkWord(Object answer, String valueStr, String checkKey) {
        ValueEntityDTO value = JSON.parseObject(valueStr, ValueEntityDTO.class);
        // 危险词列表
        List<String> dangerWordList = value.getWordList();
        if (CollUtil.isEmpty(dangerWordList)) {
            dangerWordList = new ArrayList<>();
        }
        // 需要匹配的规则
        String patternRule = dangerWordList.stream().filter(Validator::isNotEmpty).collect(Collectors.joining("|"));

        // 反词列表
        List<String> excludeList = value.getExcludeList();
        if (CollUtil.isEmpty(excludeList)) {
            excludeList = new ArrayList<>();
        }
        Map<String, Object> result = new HashMap<>();
        checkWordObject(answer, checkKey, patternRule, excludeList, result);
        return result;
    }

    /**
     * 从sourceQ中的元素中获取题目gid和选项
     * 获取题目选项
     *
     * @param sourceQ 答案
     * @return Map<String, Object> 结果 gid为题号，optionId为选项
     * @author jy.zhao
     * @date 2021/8/18 15:16
     **/
    public static QuestionOptionDTO getSourceQuestionOption(Object sourceQ) {
        // 选项返回结果
        List<OptionIdDTO> optionIdList = new ArrayList<>();
        // sourceQ对应数据库中存储的有空对象，数字等。
        if (isInstance(sourceQ, DataTypeEnum.MAP)) {
            Map<String, Object> sourceQMap = toMap(sourceQ);
            // 获取gid，如果没有，默认为""
            String qid = sourceQMap.getOrDefault(GID, Constants.BLANK).toString();
            // 数据库中存储的是数字。但是转换过来后会转成double。2->2.0  因此截取
            qid = qid.split(POINT_SEPARATOR)[0];
            optionIdList = handleSourceQMap(sourceQMap);
            return new QuestionOptionDTO(qid, optionIdList);
        } else if (isInstance(sourceQ, DataTypeEnum.STRING)) {
            String[] split = sourceQ.toString().split(UNDERLINE_SEPARATOR);
            String gid = split[0];
            // 数据库中存储的是数字。但是转换过来后会转成double。2->2.0  因此截取
            gid = gid.split(POINT_SEPARATOR)[0];
            String seq = split.length > 1 ? split[split.length - 1] : null;
            String rowId = split.length == 3 ? split[1] : null;
            if (Validator.isNotEmpty(rowId) || Validator.isNotEmpty(seq)) {
                optionIdList.add(new OptionIdDTO(rowId, seq));
                return new QuestionOptionDTO(gid, optionIdList);
            }
            return new QuestionOptionDTO(gid, optionIdList);
        } else {
            String gid = sourceQ.toString();
            // 数据库中存储的是数字。但是转换过来后会转成double。2->2.0  因此截取
            gid = gid.split(POINT_SEPARATOR)[0];
            return new QuestionOptionDTO(gid, optionIdList);
        }
    }

    /**
     * 处理sourceQ为Map类型的数据
     *
     * @param sourceQMap 问题源
     * @return List<OptionIdDTO> 返回
     * @author jy.zhao
     * @date 2021/8/29 11:47
     **/
    private static List<OptionIdDTO> handleSourceQMap(Map<String, Object> sourceQMap) {
        List<OptionIdDTO> optionIdList = new ArrayList<>();
        // 获取到问题类型
        String questionType = sourceQMap.getOrDefault(QTYPE, Constants.BLANK).toString();
        // 获取optionList
        List<Object> optionList = new ArrayList<>();
        if (isInstance(sourceQMap.get(OPTION), DataTypeEnum.LIST)) {
            optionList = toList(sourceQMap.get(OPTION));
        } else if (isInstance(sourceQMap.get(OPTION), DataTypeEnum.MAP)) {
            optionList.add(sourceQMap.get(OPTION));
        }
        if (CollUtil.isEmpty(optionList) && QUESTION_TYPE_LIST.contains(questionType)) {
            // nps或只有一个选项的打分题
            Object items = sourceQMap.get("items");
            if (isInstance(items, DataTypeEnum.LIST)) {
                optionList.add(toList(items).get(0));
            }
        }
        for (Object option : optionList) {
            // 可能为null
            if (Validator.isNull(option)) {
                continue;
            }
            String rowId = null;
            String optionId = null;
            if (isInstance(option, DataTypeEnum.LIST)) {
                List<Object> optionTmp = toList(option);
                // 级联题多级选项, 级联层数作为row_id，最后一级答案作为option_id
                rowId = String.valueOf(optionTmp.size());
                optionId = optionTmp.get(optionTmp.size() - 1).toString();
            } else if (isInstance(option, DataTypeEnum.MAP) && isInstance(toMap(option).get(GID), DataTypeEnum.STRING)) {
                String gid = getAttr(option, GID, String.class);
                if (StringUtils.isNotEmpty(gid)) {
                    String[] split = gid.split(UNDERLINE_SEPARATOR);
                    if (isEvaluateTitle(sourceQMap)) {
                        optionId = gid;
                    } else if (split.length == 2) {
                        rowId = split[0];
                        optionId = split[1];
                    } else {
                        optionId = split[0];
                    }
                }
            } else if (isInstance(option, DataTypeEnum.MAP)) {
                optionId = toMap(option).getOrDefault(GID, Constants.BLANK).toString();
            }
            if (Validator.isNotEmpty(rowId) || Validator.isNotEmpty(optionId)) {
                // 数据库中存储的是数字。但是转换过来后会转成double。2->2.0  因此截取
                if (Validator.isNotEmpty(optionId)) {
                    optionId = optionId.split(POINT_SEPARATOR)[0];
                }
                optionIdList.add(new OptionIdDTO(rowId, optionId));
            }
        }
        return optionIdList;
    }

    /**
     * 返回答案的选项
     * 对于单选题： 返回[选项id]
     * 对于多选题： 返回[选项1id, 选项2id...]
     * 对于打分题： 返回[分值]
     * 对于排序题： 返回[序号]
     * 对于级联题： 返回对应级的[选项id]
     * 对于城市题： 返回对应级的城市名称
     *
     * @param checkAnswer 答卷答案
     * @return Set<String>
     * @author jy.zhao
     * @date 2021/8/19 20:13
     **/
    public static Set<String> getOptionList(Object checkAnswer) {
        Set<String> result = new HashSet<>();
        if (isInstance(checkAnswer, DataTypeEnum.INTEGER_STRING)) {
            result.add(checkAnswer.toString());
            return result;
        }
        // 如果不是List，返回
        if (!(isInstance(checkAnswer, DataTypeEnum.LIST))) {
            return result;
        }
        for (Object ops : convertObject(checkAnswer, List.class)) {
            if (isInstance(ops, DataTypeEnum.LIST)) {
                Object o = toList(ops).get(0);
                result.add(o.toString());
            } else if (isInstance(ops, DataTypeEnum.INTEGER_DOUBLE)) {
                result.add(ops.toString());
            }
        }
        return result;
    }

    /**
     * 替换答案字符串中危险词及相反的词
     *
     * @param answer          单题答案
     * @param patternRule     危险词匹配规则
     * @param excludeWordList 反词列表
     * @return List<Object>
     * @author jy.zhao
     * @date 2021/8/19 13:47
     **/
    public static List<int[]> checkReplaceWordStr(String answer, String patternRule, List<String> excludeWordList) {
        // 替换反词列表
        for (String excludeWord : excludeWordList) {
            answer = answer.replaceAll(excludeWord, String.join("", Collections.nCopies(excludeWord.length(), "\b")));
        }
        Pattern pattern = Pattern.compile(patternRule);
        Matcher m = pattern.matcher(answer);
        List<int[]> result = new ArrayList<>();
        while (m.find()) {
            result.add(new int[]{m.start(), m.end() - m.start()});
        }
        return result;
    }

    /**
     * 解析检查对象中嵌套的危险词
     *
     * @param answer          答案
     * @param key             map中存储的结果key。 以#间隔
     * @param patternRule     匹配的危险词规则
     * @param excludeWordList 相反的反词列表
     * @param result          结果
     */
    public static void checkWordObject(Object answer, String key, String patternRule, List<String> excludeWordList, Map<String, Object> result) {
        if (isInstance(answer, DataTypeEnum.LIST)) {
            List<Object> list = toList(answer);
            for (int i = 0; i < list.size(); i++) {
                checkWordObject(list.get(i), key.concat("#").concat(String.valueOf(i)), patternRule, excludeWordList, result);
            }
        } else if (isInstance(answer, DataTypeEnum.STRING)) {
            // 答案匹配结果。int[] 长度为2. 匹配的开始位置以及匹配的长度
            List<int[]> wordPatternResult = checkReplaceWordStr((String) answer, patternRule, excludeWordList);
            if (CollUtil.isNotEmpty(wordPatternResult)) {
                result.put(key, wordPatternResult);
            }
        } else if (isInstance(answer, DataTypeEnum.MAP)) {
            Map<String, Object> subResult = null;
            if (Validator.isNotEmpty(key)) {
                subResult = new HashMap<>();
            } else {
                subResult = result;
            }
            Map<String, Object> answerMap = toMap(answer);
            for (Map.Entry<String, Object> entry : answerMap.entrySet()) {
                if (Validator.isNotEmpty(entry.getKey())) {
                    checkWordObject(entry.getValue(), String.valueOf(entry.getKey()), patternRule, excludeWordList, subResult);
                }
            }
            if (Validator.isNotEmpty(key) && MapUtil.isNotEmpty(subResult)) {
                result.put(key, subResult);
            }
        }
    }

    /**
     * 获取分数
     *
     * @param answer 答案
     * @param opSeq  答卷编号
     * @param rowId  id
     * @return Integer python使用的地方会转成int，所以在这里直接返回int
     * @author jy.zhao
     * @date 2021/8/20 10:50
     **/
    public static Integer getScore(Object answer, String opSeq, String rowId) {
        int result = 0;
        try {
            if (Validator.isNotEmpty(opSeq)) {
                if (Validator.isNotEmpty(rowId)) {
                    if (isInstance(answer, DataTypeEnum.MAP) && isInstance(toMap(answer).get(rowId), DataTypeEnum.MAP)) {
                        answer = toMap(toMap(answer).get(rowId)).get(opSeq);
                        if (isInstance(answer, DataTypeEnum.LIST) && isInstance(toList(answer).get(0), DataTypeEnum.INTEGER_DOUBLE_STRING)) {
                            result = Integer.parseInt(toList(answer).get(0).toString());
                        } else if (isInstance(answer, DataTypeEnum.INTEGER_DOUBLE_STRING)) {
                            result = Integer.parseInt(answer.toString());
                        }
                    }
                } else if (isInstance(answer, DataTypeEnum.MAP) && isInstance(toMap(answer).get(opSeq), DataTypeEnum.LIST) && isInstance(toList(toMap(answer).get(opSeq)).get(0), DataTypeEnum.INTEGER_DOUBLE_STRING)) {
                    result = Integer.parseInt(toList(toMap(answer).get(opSeq)).get(0).toString());
                }
            } else {
                if (isInstance(answer, DataTypeEnum.LIST) && !toList(answer).isEmpty() && isInstance(toList(answer).get(0), DataTypeEnum.INTEGER_DOUBLE_STRING)) {
                    result = Integer.parseInt(toList(answer).get(0).toString());
                }
            }
        } catch (Exception e) {
            log.error("计算分数出错，answer={},opSeq={}, rowId={}", answer, opSeq, rowId, e);
            result = 0;
        }
        return Validator.isNotNull(result) ? result : 0;
    }

    /**
     * 从sourceQ中的元素中获取题目gid
     *
     * @param sourceQ 答案
     * @return String
     * @author jy.zhao
     * @date 2021/8/20 22:47
     **/
    public static String getQuestionId(Object sourceQ) {
        if (Validator.isNull(sourceQ)) {
            return Constants.UNSPECIFIED_GID;
        }
        String gid;
        if (isInstance(sourceQ, DataTypeEnum.MAP)) {
            gid = toMap(sourceQ).getOrDefault(GID, Constants.UNSPECIFIED_GID).toString();
        } else {
            gid = sourceQ.toString();
        }
        // 数据库中存储的是数字。但是转换过来后会转成double。2->2.0  因此截取
        gid = gid.split(POINT_SEPARATOR)[0];
        return gid;
    }

    /**
     * 判断sourceQ中的元素是否选择题
     *
     * @param sourceQ 答案
     * @return boolean
     * @author jy.zhao
     * @date 2021/8/20 21:49
     **/
    public static boolean isChoiceTitle(Object sourceQ) {
        return TitleTypeEnum.MATRIX_SINGLE.getName().equals(getAttr(sourceQ, QTYPE, String.class)) ||
                TitleTypeEnum.SORT.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否 矩阵单选题
     *
     * @param sourceQ 题目
     * @return boolean
     * @author jy.zhao
     * @date 2021/9/1 19:11
     **/
    public static boolean isMatrixSingle(Object sourceQ) {
        return TitleTypeEnum.MATRIX_SINGLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否 矩阵多选题
     *
     * @param sourceQ 题目
     * @return boolean
     * @author jy.zhao
     * @date 2021/9/1 19:11
     **/
    public static boolean isMatrixMultiple(Object sourceQ) {
        return TitleTypeEnum.MATRIX_MULTIPLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否多选
     *
     * @param sourceQ 题目
     * @return boolean
     * @author jy.zhao
     * @date 2021/9/1 19:23
     **/
    public static boolean isMultiple(Object sourceQ) {
        return TitleTypeEnum.MULTIPLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否评价题
     *
     * @param sourceQ 答案
     * @return boolean
     * @author jy.zhao
     * @date 2021/8/20 21:41
     **/
    public static boolean isEvaluateTitle(Object sourceQ) {
        return TitleTypeEnum.EVALUATION.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否城市地理题
     *
     * @param sourceQ 答案
     * @return Boolean
     * @author jy.zhao
     * @date 2021/8/20 21:44
     **/
    public static boolean isCityTitle(Object sourceQ) {
        return TitleTypeEnum.CITY.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否单多选
     *
     * @param sourceQ 答案
     * @return Boolean
     * @author jy.zhao
     * @date 2021/8/20 21:44
     **/
    public static boolean isSingleMultipleTitle(Object sourceQ) {
        return TitleTypeEnum.SINGLE.getName().equals(getAttr(sourceQ, QTYPE, String.class)) ||
                TitleTypeEnum.MULTIPLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否单多选
     *
     * @param sourceQ 答案
     * @return Boolean
     * @author jy.zhao
     * @date 2021/8/20 21:44
     **/
    public static boolean isSingleTitle(Object sourceQ) {
        return TitleTypeEnum.SINGLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 校验分数是否在限制范围内
     *
     * @param score         分数
     * @param scoreRangeDTO 限制
     * @return Boolean
     * @author jy.zhao
     * @date 2021/8/19 22:03
     **/
    public static boolean validScoreRange(int score, ScoreRangeDTO scoreRangeDTO) {
        if (Validator.isNull(scoreRangeDTO)) {
            return false;
        }
        // 最大分数和最小分数
        Integer minScore = scoreRangeDTO.getMinScore();
        Integer maxScore = scoreRangeDTO.getMaxScore();
        // 左边界和右边界
        boolean leftOpen = false;
        boolean rightOpen = false;
        if (Validator.isNotNull(scoreRangeDTO.getLeftOpen())) {
            leftOpen = scoreRangeDTO.getLeftOpen();
        }
        if (Validator.isNotNull(scoreRangeDTO.getRightOpen())) {
            rightOpen = scoreRangeDTO.getRightOpen();
        }
        // 左边界决定是大于等于还是大于。右边界同理
        boolean leftLimit = leftOpen ? score > minScore : score >= minScore;
        boolean rightLimit = rightOpen ? score < maxScore : score <= maxScore;
        return leftLimit && rightLimit;
    }

    /**
     * 处理OptionIdDTO。
     *
     * @param optionIdDTO 选项id
     * @return Object
     * @author jy.zhao
     * @date 2021/9/1 16:02
     **/
    public static Object dumpOption(OptionIdDTO optionIdDTO) {
        if (Validator.isNotEmpty(optionIdDTO.getRow_id())) {
            return optionIdDTO;
        }
        return optionIdDTO.getOption_id();
    }

    /**
     * 判断答案是否在区间范围内
     *
     * @param answerSecond   答案，转换为秒后的答案
     * @param timeRangeLimit 开始时间和结束时间范围限制
     * @return boolean
     * @author jy.zhao
     * @date 2021/8/19 14:54
     **/
    public static boolean validateTimeRange(int answerSecond, List<String> timeRangeLimit, Integer type) {
        if (CollUtil.isEmpty(timeRangeLimit) || timeRangeLimit.size() < 2) {
            return false;
        }
        // 1. 得到开始时间和结束时间
        LocalDateTime beginTime;
        LocalDateTime endTime;
        try {
            // 1.1 如果不是时间格式，getLocalDateTime可能会报错。
            beginTime = DateUtil.getLocalDateTime(timeRangeLimit.get(0));
            endTime = DateUtil.getLocalDateTime(timeRangeLimit.get(1));
        } catch (DateException e) {
            log.error("[ticket] 工单创建 时间转换错误。开始结束时间：{}， 校验秒数： {}", timeRangeLimit, answerSecond);
            return false;
        }
        // 1.2 如果转换结果为null, 报错
        if (Objects.isNull(beginTime) || Objects.isNull(endTime)) {
            log.error("[ticket] 工单创建 时间转换错误。开始结束时间：{}， 校验秒数： {}", timeRangeLimit, answerSecond);
            return false;
        }
        long beginSecond;
        long endSecond;
        // 2. 根据类型得到对应的秒 time：时分秒转为秒。 date：年月日时分秒转为秒
        if (SurveyTitleTypeEnum.TIME.getIndex().equals(type)) {
            beginSecond = DateUtil.hmsToSecond(beginTime);
            endSecond = DateUtil.hmsToSecond(endTime);
        } else {
            beginSecond = DateUtil.toSecond(beginTime);
            endSecond = DateUtil.toSecond(endTime);
        }
        // 3. 判断答案是否在区间范围内.是，通过校验。
        return answerSecond >= beginSecond && answerSecond <= endSecond;
    }

    /**
     * 字符串转为JSONObject
     *
     * @param jsonStr json格式的字符串
     * @return JSONObject JSONObject
     * @author jy.zhao
     * @date 2021/8/25 20:41
     **/
    public static JSONObject toJSONObject(String jsonStr) {
        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }
        // 校验是否符合JSON
        JSONObject jsonObject = null;
        boolean validate = JSONValidator.from(jsonStr).validate();
        if (validate) {
            try {
                jsonObject = JSON.parseObject(jsonStr);
            } catch (JSONException e) {
                log.error("JSON字符串转换为对象异常, jsonStr={}", jsonStr);
            }
        }
        return jsonObject;
    }

    /**
     * 字符串转为JSONObject
     *
     * @param jsonStr json格式的字符串
     * @return JSONObject JSONObject
     * @author jy.zhao
     * @date 2021/8/25 20:41
     **/
    private static JSONArray toJSONArray(String jsonStr) {
        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }
        // 校验是否符合JSON
        JSONArray jsonArray = null;
        boolean validate = JSONValidator.from(jsonStr).validate();
        if (validate) {
            try {
                jsonArray = JSON.parseArray(jsonStr);
            } catch (JSONException e) {
                log.error("JSON字符串转换为数组异常, jsonStr={}", jsonStr);
            }
        }
        return jsonArray;
    }

    /**
     * 转换对象.在使用前使用 isInstance 校验类型
     *
     * @param obj   对象
     * @param clazz 类型
     * @return T 类型
     * @author jy.zhao
     * @date 2021/8/27 23:27
     **/
    public static <T> T convertObject(Object obj, Class<T> clazz) {
        return clazz.cast(obj);
    }

    /**
     * 转换对象.在使用前使用 isInstance 校验类型
     *
     * @param obj 对象
     * @return List<Object> 结果
     * @author jy.zhao
     * @date 2021/8/27 23:27
     **/
    public static List<Object> toList(Object obj) {
        return (List<Object>) obj;
    }

    /**
     * 转换对象.在使用前使用 isInstance 校验类型
     *
     * @param obj 对象
     * @return Map<String, Object> 结果
     * @author jy.zhao
     * @date 2021/8/27 23:27
     **/
    public static Map<String, Object> toMap(Object obj) {
        return (Map<String, Object>) obj;
    }

    /**
     * 根据key获取对象中的数值
     *
     * @param obj   对象
     * @param key   key
     * @param clazz 类型
     * @return T
     * @author jy.zhao
     * @date 2021/8/27 19:55
     **/
    public static <T> T getAttr(Object obj, String key, Class<T> clazz) {
        // 如果不是map，直接返回null
        if (!(isInstance(obj, DataTypeEnum.MAP))) {
            return null;
        }
        // 获取到key对应的值
        T cast;
        Object value = ((Map) obj).get(key);
        try {
            cast = clazz.cast(value);
        } catch (ClassCastException e) {
            cast = null;
        }
        return cast;
    }

    /**
     * 判断类型
     *
     * @param object       需要判断的对象
     * @param dataTypeEnum 类型枚举
     * @return boolean
     * @author jy.zhao
     * @date 2021/8/27 19:02
     **/
    public static boolean isInstance(Object object, DataTypeEnum dataTypeEnum) {
        if (dataTypeEnum.getIndex() == DataTypeEnum.INTEGER.getIndex()) {
            return object instanceof Integer;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.STRING.getIndex()) {
            return object instanceof String;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.DOUBLE.getIndex()) {
            return object instanceof Double;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.LIST.getIndex()) {
            return object instanceof List;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.MAP.getIndex()) {
            return object instanceof Map;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.BIG_DECIMAL.getIndex()) {
            return object instanceof BigDecimal;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.INTEGER_STRING.getIndex()) {
            return object instanceof Integer || object instanceof String;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.INTEGER_DOUBLE.getIndex()) {
            return object instanceof Integer || object instanceof Double;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.INTEGER_DOUBLE_STRING.getIndex()) {
            return object instanceof Integer || object instanceof Double || object instanceof String;
        }
        return false;
    }
}
